#ifndef __PNSESSION_H__
#define __PNSESSION_H__

/**
*@file       pnsession.h
*@brief      session class
*
*@author     graywind
*@version    1.0.0.0
*@data       2011/11/16
*/

#include <string.h>
#include <queue>
#include "boost/asio.hpp"
#include "boost/thread.hpp"
#include "boost/function.hpp"
#include "boost/shared_ptr.hpp"
#include "boost/enable_shared_from_this.hpp"
#include "pnmsgdef.h"
#include "pncycledbuffer.hpp"

///net service relative
namespace pnNetlib
{
	class pnSession : public boost::enable_shared_from_this<pnSession>
	{
	public:
		typedef boost::shared_ptr<pnSession>  SessionPtr;

	public:
		typedef  pnCycledBuffer<char>         BufferType;

		enum { wait_send = 0 };
		enum { proc_send = 1 };

	private:
        ///@brief     whether this session alive
        bool                                  m_bIsAlive;
		///@brief     session id
		int                                   m_iID;
		///@brief     state of send msg
		int                                   m_iState;
        ///@brief     usage type
		eUsageType                            m_eType;
		///@brief     message call back
		MsgProc                               m_msgProc;
		///@brief     close call back
		boost::function<void()>               m_closeProc;
		///@brief     socket object
		boost::asio::ip::tcp::socket          m_socket;
		///@brief     read buffer
		BufferType                            m_readbuffer;
		///@brief     write buffer
		BufferType                            m_writebuffer;
		///@brief     mutex object
		boost::mutex                          m_mutex;
		///@brief     valid length of buffer
		std::queue<long>                      m_validLength;

	public:
		///@brief     constructor
		///param[in]  id              session id
		///param[in]  ioService       io service object ref 
        ///param[in]  buffer_length   session buffer length
		pnSession(int id, boost::asio::io_service& ioService , long buffer_length);
		///@brief     destructor
		~pnSession();

	private:
		///@brief     send msg
		bool          send_msg(long bytes_len);
		///@brief     callback function taht after send msg
		void          send_handler(const boost::system::error_code& ec, std::size_t bytes_transferred);
		///@brief     callback function that after connect to the server
		void          connect_handler(const boost::system::error_code& ec);

		///@brief     callback function after read header
		void          read_header(const boost::system::error_code& ec, std::size_t bytes_transferred);
		///@brief     callback function after read body
		void          read_body(unsigned long msg_id, const boost::system::error_code& ec, std::size_t bytes_transferred);

        ///@brief     start session
		///retval     void
		void          start();

	public:
		///@brief        started yet(read-only)
		///retval        bool
		const bool       is_open() const;

		///@brief        get session id
		///retval        int
		const int        get_id() const;

        ///@brief        get session usage
        ///retval        eUsageType
        const eUsageType get_type() const;

		///@brief        start session
        ///param[in]     type       usage type
		///retval        void
		void             start(eUsageType type);

		///@brief        push msg
		///param[in]     pHeader  pointer of msg header
		///param[in]     pMsg     msg buffer head pointer
		///retval        bool
		bool             push_msg(stPnMsgHeader* pHeader, void* pMsg);

		///@brief        connect to acceptor, if succeed then call start()
		///param[in]     ip         ip address connect to
		///param[in]     port       port connect to
		///retval        void
		void             connect(const std::string& ip, unsigned short port);

		///@brief        close session
		void             close();

		///@brief        set callback function
		void             set_msg_proc(MsgProc msgProc);
		///@brief        set callback function
		void             set_close_proc(boost::function<void(void)>   f);

	public:
		///@brief     get socket object
		boost::asio::ip::tcp::socket& get_socket();
	};

	/***************************************************************************************************************************/
	inline
	const
	int
	pnSession::get_id() const
	{
		return m_iID;
	}

	/***************************************************************************************************************************/
	inline
	const
	bool
	pnSession::is_open() const
	{
		return m_bIsAlive;
	}

	/***************************************************************************************************************************/
	inline
	boost::asio::ip::tcp::socket&
	pnSession::get_socket()
	{
		return m_socket;
	}

    /***************************************************************************************************************************/
    inline
    const
    eUsageType
    pnSession::get_type() const
    {
        return m_eType;
    }
};

#endif